package cn.walls1717.juc.tl;

import lombok.extern.slf4j.Slf4j;

import java.lang.ref.*;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

/**
 * 引用测试<br>
 * <br>
 *
 * 强引用(默认)：<br>
 * 当内存不足时，JVM开始进行垃圾回收，对于强引用的对象，就算是出现了OOM，也不会对该对象进行回收。 只要这个对象是可达状态，即使以后再也用不到该对象也永远不会被JVM回收，这就是内存泄漏的主要原因之一。<br>
 * <br>
 *
 * 软引用：<br>
 * 当内存充足时不会被回收，当内存不足时会被回收。<br>
 * <br>
 *
 * 弱引用：<br>
 * 只要发生gc，不管内存是否足够都会被回收。<br>
 * <br>
 *
 * 虚引用：<br>
 * 虚引用不能单独使用，不能直接通过它访问对象，必须和引用队列一起联合使用，在任何时候都有可能被gc，在对象被gc之后，会被放在引用队列中。<br>
 * phantomReference的get总是返回null，虚引用的主要作用就是跟踪对象被垃圾回收的状态，仅仅是提供了 一种确保对象被finalize以后，做某些事情的通知机制。<br>
 * 设置虚引用的唯一目的就是这个对象被gc之后，会收到一个通知或者后续添加进一步的处理，用来实现比finalize 更加灵活的回收操作。<br>
 *
 * @author walls1717
 * @create 2022/12/14 20:39
 **/
@Slf4j
public class ReferenceDemo {

    public static void main(String[] args) throws InterruptedException {
        // strongReference();
        // softReference();
        // weakReference();

        ReferenceQueue<MyObject> queue = new ReferenceQueue<>();
        MyObject myObject = new MyObject();
        PhantomReference<MyObject> phantomReference = new PhantomReference<>(myObject, queue);
        // log.info(phantomReference.get() + "");

        ArrayList<byte[]> bytes = new ArrayList<>();

        new Thread(()->{
            while (true) {
                bytes.add(new byte[1 * 1024 * 1024]);
                log.info(phantomReference.get() + " list add success");
                try {
                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t1").start();

        new Thread(()->{
            while (true) {
                Reference<? extends MyObject> poll = queue.poll();
                if (poll != null) {
                    log.info("对象被gc并放入队列");
                    break;
                }
            }
        }, "t2").start();
    }

    /**
     * 弱引用
     *
     * @throws InterruptedException 。
     */
    private static void weakReference() throws InterruptedException {
        WeakReference<MyObject> weakReference = new WeakReference<>(new MyObject());
        log.info("gc before 内存够用 " + weakReference.get());
        System.gc();
        TimeUnit.SECONDS.sleep(1);
        log.info("gc after 内存够用 " + weakReference.get());
    }

    /**
     * 软引用
     *
     * @throws InterruptedException .
     */
    private static void softReference() throws InterruptedException {
        SoftReference<MyObject> myObject = new SoftReference<>(new MyObject());
        System.gc();
        TimeUnit.SECONDS.sleep(1);
        log.info("gc after 内存够用 " + myObject.get());

        try {
            byte[] bytes = new byte[20 * 1024 * 1024];
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            log.info("gc after 内存不够 " + myObject.get());
        }
    }

    /**
     * 强引用，new对象默认就是这个。
     */
    private static void strongReference() {
        MyObject myObject = new MyObject();
        log.info("gc before " + myObject);
        myObject = null;
        // 手动gc，一般不用。
        System.gc();
        log.info("gc after " + myObject);
    }
}

@Slf4j
class MyObject {

    /**
     * 正常情况下这个方法不用重写，jvm会自己决定什么时候需要回收垃圾。
     *
     * @throws Throwable .
     */
    @Override
    protected void finalize() throws Throwable {
        log.warn("--> invoke finalize method <--");
    }
}
